package beefs.lowlevel;

import static org.junit.Assert.assertFalse;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import org.designwizard.design.ClassNode;
import org.designwizard.design.MethodNode;
import org.designwizard.exception.InexistentEntityException;
import org.designwizard.exception.NotAnInterfaceException;
import org.designwizard.main.DesignWizard;
import org.junit.Test;

import beefs.common.Mappings;

//TODO implement
public class DSTTest {

	private DesignWizard dw;
	
	public DSTTest() {
		try {
			this.dw = new DesignWizard("resources/allclasses");
		} catch (IOException e) {}
	}
	
	@Test
	// Replica instances are not allowed to depend on Group
	public void testGroupReplica() throws InexistentEntityException, NotAnInterfaceException {
		
		System.out.println("---" + Mappings.REPLICA_CLASS + " violations");
		
		Set<ClassNode> replicaSet = new HashSet<ClassNode>();
		ClassNode replica = dw.getClass(Mappings.REPLICA_CLASS);
		replicaSet.add(replica);
		replicaSet.addAll(replica.getEntitiesThatImplements());

		Set<ClassNode> groupSet = new HashSet<ClassNode>();
		ClassNode groupClass = dw.getClass(Mappings.REPLICATION_GROUP_CLASS);
		groupSet.add(groupClass);
		groupSet.addAll(groupClass.getEntitiesThatImplements());
		
		boolean fail = false;
		for (ClassNode rep : replicaSet) {
			Set<ClassNode> calleeClasses = rep.getCalleeClasses();
			for (ClassNode callee : calleeClasses) {
				if (groupSet.contains(callee)) {
					fail = true;
					System.out.println(rep.getName() + "is_accessing" + callee);
				}
			}
		}
		
		System.out.println();
		assertFalse(fail);
		
	}
	
	@Test
	// Only Replica Factory must create Replica instances.
	public void testReplicaFactory() throws InexistentEntityException, NotAnInterfaceException {
		System.out.println("---" + Mappings.REPLICA_FACTORY_INTERFACE + " violations");
		
		ClassNode replica = dw.getClass(Mappings.REPLICA_CLASS);
		ClassNode replicaFactory = dw.getClass(Mappings.REPLICA_FACTORY_INTERFACE);
		
		Set<ClassNode> entitiesThatImplements = replica.getEntitiesThatImplements();
		
		boolean fail = false;
		
		for (ClassNode c : entitiesThatImplements) {
			
			Set<MethodNode> constructors = c.getConstructors();
			
			for (MethodNode constructor : constructors) {
				
				for ( ClassNode caller : constructor.getCallerClasses()) {
					
					if (!caller.equals(replicaFactory) && 
							!caller.implementsInterface(replicaFactory)) {
						fail = true;
						System.out.println(caller.getName() + "is_accessing" + constructor.getName());
					}
					
				}
				
				
			}

		}
		
		System.out.println();
		assertFalse(fail);
	}
	
	
	@Test
	public void testReplicationGroup() throws InexistentEntityException, NotAnInterfaceException {
		System.out.println("---" + Mappings.REPLICATION_GROUP_CLASS + " violations");

		Set<ClassNode> groupSet = new HashSet<ClassNode>();
		ClassNode groupClass = dw.getClass(Mappings.REPLICATION_GROUP_CLASS);
		groupSet.add(groupClass);
		groupSet.addAll(groupClass.getEntitiesThatImplements());
		
		Set<MethodNode> constructors = new HashSet<MethodNode>();
		for (ClassNode c : groupSet) {
			constructors.addAll(c.getConstructors());
		}
		
		ClassNode replicationFactory = this.dw.getClass(Mappings.REPLICATION_GROUP_FACTORY_INTERFACE);
		Set<ClassNode> allowed = new HashSet<ClassNode>();
		allowed.add(replicationFactory);
		allowed.addAll(replicationFactory.getEntitiesThatImplements());
		
		boolean fail = false;
		for (MethodNode constructor : constructors) {
			
			Set<ClassNode> callerClasses = constructor.getCallerClasses();
			for (ClassNode c : callerClasses)
				if(!allowed.contains(c)) {
					fail = true;
					System.out.println(c.getName() + "is_accessing" + constructor.getName());
				}
			
		}
	
		System.out.println();
		assertFalse(fail);
		
		
	}
	
}
